package de.tudarmstadt.kom.androidsensorapi.sensormanagement;

import android.app.Activity;
import android.content.Context;
import android.hardware.SensorManager;

import java.util.HashMap;
import java.util.Map;

import de.tudarmstadt.kom.androidsensorapi.sensor.data.AbstractSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.AbstractSensor;
import de.tudarmstadt.kom.androidsensorapi.sensormanagement.sensorinterface.SensorDataListener;
import de.tudarmstadt.kom.androidsensorapi.sensormanagement.sensorlistenerholder.SensorListenerHolder;

/**
 * Created by Tobias Welther
 */
public class SimpleSensorManager {

    /**
     * This is the list of registered Sensors.
     */
    private Map<SensorTypeToData,SensorListenerHolder> registeredSensors;


    /**
     * Creates the sensor Management.
     * It creates all Sensors and all associations.
     *
     * No Sensors are Registered on Startup.
     */
    public SimpleSensorManager() {
        registeredSensors = new HashMap<SensorTypeToData, SensorListenerHolder>();

        for(SensorTypeToData types : SensorTypeToData.setOfAllSensors){
            SensorListenerHolder listenerHolder = new SensorListenerHolder(types.getSensorDataClass());
        }
    }


    /**
     * A sensor has received an Update.
     * Let's broadcast it to all the listeners.
     *
     * @param data the SensorData to process
     */
    public void receivedSensorData(AbstractSensorData data){
        SensorTypeToData type = SensorTypeToData.getTypeByDataClass(data.getClass());
        if(type != SensorTypeToData.NONE){
            synchronized (type){
                SensorListenerHolder holder = registeredSensors.get(type);
                if(holder != null){
                    holder.queueSensorEvent(data);
                }
            }
        }
    }


    /**
     * Registers a SensorDataListener to the System.
     * The Listener will receive Updates to all Methods Annotated with:
     * {@link de.tudarmstadt.kom.androidsensorapi.sensormanagement.sensorlistenerholder.SensorData}.
     * The Method must have one Variable: a subclass of {@link AbstractSensorData}.
     *
     * for Example:
     *
     *
     * @SensorData()
     * public void hereComeLocationUpdates(BrightnessSensorData data){
     *     float siLuxBrightnessValue = data.getSiLuxValue();
     *     ....
     * }
     *
     * The new Data is always called from a new Thread.
     * This allows multi-threading use of receiving this data.
     *
     * WARNING: don't forget, the calling Thread does NOT run on UI Thread!
     *
     * @param listener the listener to search for methods to call on data update.
     */
    public void registerSensorListener(SensorDataListener listener){
        for(SensorListenerHolder listenerHolder : registeredSensors.values()){
            listenerHolder.registerListener(listener);
        }
    }


    /**
     * Removes a SensorDataListener. This means he will not longer get any Updates for any
     * SensorData.
     *
     * @param listener to remove.
     */
    public void removeSensorListener(SensorDataListener listener){
        for(SensorListenerHolder listenerHolder : registeredSensors.values()){
            listenerHolder.removeListener(listener);
        }
    }


    /**
     * Lets the passed sensor Type start working.
     * This implicates incoming Data updates to all Listeners.
     *
     * If {@link SensorTypeToData#All} is passed, all Sensors will be linked.
     * WARNING: This can drain the battery faster than Chernobyl!
     *
     * If {@link SensorTypeToData#NONE} is passed, nothing will happen.
     *
     * WARNING: Both Parameters MAY NOT! Be Null.
     *
     * @param sensorToStartWorking the sensor to start working.
     * @param activityToRemoveFrom the Activity to call the remove process from
     */
    public void startSensor(SensorTypeToData sensorToStartWorking, Activity activityToRemoveFrom){
        if(sensorToStartWorking == null || activityToRemoveFrom == null) return;

        SensorManager manager = (SensorManager) activityToRemoveFrom.getSystemService(Context.SENSOR_SERVICE);

        if(sensorToStartWorking == SensorTypeToData.NONE) return;
        if(sensorToStartWorking == SensorTypeToData.All){
            for(SensorTypeToData type : SensorTypeToData.setOfAllSensors){
                startSensor(type, activityToRemoveFrom);
            }

            return;
        }

        AbstractSensor sensor = sensorToStartWorking.getSensor();
        SensorListenerHolder holder = this.registeredSensors.get(sensorToStartWorking);

        if(sensor.isRegistered()) return;

        sensor.register(manager);
        holder.startDequeueing();
    }


    /**
     * Lets the passed sensor Type stop.
     * This implicates incoming Data updates to all Listeners will stop.
     *
     * If {@link SensorTypeToData#All} is passed, all Sensors will be shutdown.
     *
     * If {@link SensorTypeToData#NONE} is passed, nothing will happen.
     *
     * @param sensorToStop the sensor to stop
     * @param activityToRegisterTo to call the Sensor init from
     */
    public void stopSensor(SensorTypeToData sensorToStop, Activity activityToRegisterTo){
        if(sensorToStop == null || activityToRegisterTo == null) return;

        SensorManager manager = (SensorManager) activityToRegisterTo.getSystemService(Context.SENSOR_SERVICE);

        if(sensorToStop == SensorTypeToData.NONE) return;
        if(sensorToStop == SensorTypeToData.All){
            for(SensorTypeToData type : SensorTypeToData.setOfAllSensors){
                stopSensor(type, activityToRegisterTo);
            }

            return;
        }

        AbstractSensor sensor = sensorToStop.getSensor();
        SensorListenerHolder holder = this.registeredSensors.get(sensorToStop);

        if(!sensor.isRegistered()) return;

        sensor.unregister(manager);
        holder.stopDequeueing();
    }
}
